Today’s Objectives
- Download and load microarray dataset into R
- Explore the dataset with basic visualizations
- Identify differentially expressed genes (DEGs)
- Generate annotation of the DEGs
Cleft Lip and Palate 1/3
Cleft lip and cleft palate (CLP) are splits in the upper lip, the roof of the mouth (palate) or both. They result when facial structures that are developing in an unborn baby do not close completely. CLP is one of the most common birth defects with a frequency of 1/700 live births.
Cleft Lip and Palate 2/3
Children with cleft lip with or without cleft palate face a variety of challenges, depending on the type and severity of the cleft.
Difficulty feeding. One of the most immediate concerns after birth is feeding.
Ear infections and hearing loss. Babies with cleft palate are especially at risk of developing middle ear fluid and hearing loss.
Dental problems. If the cleft extends through the upper gum, tooth development may be affected.
Speech difficulties. Because the palate is used in forming sounds, the development of normal speech can be affected by a cleft palate. Speech may sound too nasal.
Reference: Mayo Foundation for Medical Education and Research
Cleft Lip and Palate 3/3
DNA variation in Interferon Regulatory Factor 6 (IRF6) causes Van der Woude syndrome (VWS)
VWS is the most common syndromic form of cleft lip and palate.
However, the causing variant in IRF6 has been found in only 70% of VWS families!
IRF6 is a transcription factor with a conserved helix-loop-helix DNA binding domain and a less well-conserved protein binding domain.
Reference: Hum Mol Genet. 2014 May 15; 23(10): 2711–2720
Question
Given:
The pathogenic variant in IRF6 exists in only 70% of the VWS families
IRF6 is a transcription factor
How can we identify other genes that might be involved in the remaining 30% of the VWS families?
Hint
Usually, genes that are regulated by a transcription factor belong to the same biological process or pathway.
Therefore, by comparing the gene expression patterns between wild-type (functional) Irf6 and knockout (non-functional) Irf6, it could be possible to identify genes that are regulated (targeted) by Irf6.
Hypothesis
\(H_O : \mu_{WT} = \mu_{KO}\)
\(H_A : \mu_{WT} \ne \mu_{KO}\)
Where \(\mu\) is the mean of the gene expression values of a gene.
One-sided or Two-sided testing?

Why Microarray?
It does not require a predefinied set of candidate genes
It requires only a small amount of RNA
It is a high-throughput protocol - transcriptome-wide analysis
One experiment can generate multiple hypotheses
Experimental Design
- 3 IRF6 wild-type (+/+) and 3 knockout (-/-) mouse embryos.
- E17.5 embryos were removed from euthanized mothers.
- Skin was removed from embryos.
- Total RNA was isolated from the skin.
- Resultant RNA was hybridized to Affymetrix GeneChip Mouse Genome 430 2.0 arrays.

Dataset
- The original dataset can be obtained from NCBI GEO with accession GSE5800
Loading
We are going to load the dataset from a tsv file (Irf6.tsv) into a variable called data using function read.table.
data here is just an arbitrary varilable name to hold the result of read.table and it can be called/named almost anything. See The State of Naming Conventions in R (Bååth 2012) for more information on naming varilables in R.
# Load data from text file into a varilable
data = as.matrix(read.table("Irf6.tsv", header = TRUE, row.names = 1))
Note: the hash sign (#) indicates that what comes after is a comment. Comments are for documentation and readability of the R code and they are not evaluated (or executed).
Checking
dim(data) # Dimension of the dataset
[1] 45101 6
head(data) # First few rows
KO1 KO2 KO3 WT1 WT2 WT3
1415670_at 6531.0 5562.8 6822.4 7732.1 7191.2 7551.9
1415671_at 11486.3 10542.7 10641.4 10408.2 9484.5 7650.2
1415672_at 14339.2 13526.1 14444.7 12936.6 13841.7 13285.7
1415673_at 3156.8 2219.5 3264.4 2374.2 2201.8 2525.3
1415674_a_at 4002.0 3306.9 3777.0 3760.6 3137.0 2911.5
1415675_at 3468.4 3347.4 3332.9 3073.5 3046.0 2914.4
Exploring
Check the behavior of the data (e.g., normal?, skewed?)
hist(data, col = "gray", main="Histogram")

Multiple Plots 1/2
samples = colnames(data2) # Headers (names) of the columns
samples
[1] "KO1" "KO2" "KO3" "WT1" "WT2" "WT3"
par( mfrow = c( 2, 3 ) ) # Split screen into 2 rows x 3 columns paritions
for (i in 1:3) {
# for each of the first 3 columns in the table
hist(data2[,i], col = "red", main = samples[i])
}
for (i in 4:6) {
# for each of the last 3 columns in the table
hist(data2[,i], col = "green", main = samples[i])
}
Multiple Plots 2/2

par( mfrow = c( 1, 1 ) ) # Just to set screen back to 1 partition
Boxplot
colors = c(rep("red", 3), rep("green", 3))
boxplot(data2, col = colors, las = 2)

Clustering 1/2
Hierarchical clustering of the samples (i.e., columns) based on the correlation coefficients of the expression values
hc = hclust(as.dist(1 - cor(data2)))
plot(hc)

Clustering 2/2
To learn about a function (e.g., hclust), you may type ?function (e.g., ?hclust) in the console to launch R documentation on that function:
Comparing
We are going to compare the means of the replicates of the two conditions
# Compute the means of the samples of each condition
ko = apply(data2[, 1:3], 1, mean)
head(ko)
1415670_at 1415671_at 1415672_at 1415673_at 1415674_a_at 1415675_at
12.61692 13.40966 13.78313 11.47096 11.84693 11.72381
wt = apply(data2[, 4:6], 1, mean)
head(wt)
1415670_at 1415671_at 1415672_at 1415673_at 1415674_a_at 1415675_at
12.87043 13.15269 13.70450 11.20664 11.66649 11.55578
Scatter

Differentially Expressed Genes (DEGs)
To identify DEGs, we will identify:
- Biologically significantly differentially expressed
- Statistically significantly differentially expressed
Then, we will take the overlap (intersection) of the two sets
Biological Significance (fold-change) 1/2
fold = ko - wt # Difference between means
head(fold)
1415670_at 1415671_at 1415672_at 1415673_at 1415674_a_at 1415675_at
-0.25351267 0.25697097 0.07863227 0.26431191 0.18044345 0.16803065
- What do the positive and negative values of the fold-change indicate? Considering the
WT condition is the reference (or control)
- +ve \(\rightarrow\) Up-regulation \(\uparrow\)
- -ve \(\rightarrow\) Down-regulation \(\downarrow\)
Biological Significance (fold-change) 2/2
hist(fold, col = "gray") # Histogram of the fold

Statistical Significance (p-value) 1/3
- To assess the statistical significance of the difference in the expression values for each gene between the two conditions (e.g.,
WT and KO), we are going to use t-test.

t-test
Let’s say there are two samples x and y from the two populations, X and Y, respectively, to determine whether the means of two populations are significantly different, we can use t.test.
?t.test
t-test : Example 1
x = c(4, 3, 10, 7, 9) ; y = c(7, 4, 3, 8, 10)
t.test(x, y)
Welch Two Sample t-test
data: x and y
t = 0.1066, df = 7.9743, p-value = 0.9177
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
-4.12888 4.52888
sample estimates:
mean of x mean of y
6.6 6.4
t.test(x, y)$p.value
[1] 0.917739
t-test : Example 2
x = c(6, 8, 10, 7, 9) ; y = c(3, 2, 1, 4, 5)
t.test(x, y)
Welch Two Sample t-test
data: x and y
t = 5, df = 8, p-value = 0.001053
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
2.693996 7.306004
sample estimates:
mean of x mean of y
8 3
t.test(x, y)$p.value
[1] 0.001052826
Statistical Significance (p-value) 2/3
pvalue = NULL # Empty list for the p-values
n = nrow(data) # Number of genes (rows)
n
[1] 45101
for(i in 1 : n) {
# for each gene
x = data2[i, 1:3] # wt values of gene number i
y = data2[i, 4:6] # ko values of gene number i
t = t.test(x, y) # t-test between the two conditions
pvalue[i] = t$p.value # Put current p-value in the list of p-values
}
head(pvalue)
[1] 0.092706280 0.182663337 0.129779075 0.272899180 0.262377176 0.005947807
Statistical Significance (p-value) 3/3
hist(-log10(pvalue), col = "gray") # Histogram of p-values (-log10)

Volcano : Statistical & Biological 1/3
plot(-log10(pvalue) ~ fold)

Volcano : Statistical & Biological 2/3
fold_cutoff = 2
pvalue_cutoff = 0.01
plot(-log10(pvalue) ~ fold)
abline(v = fold_cutoff, col = "blue", lwd = 3)
abline(v = -fold_cutoff, col = "red", lwd = 3)
abline(h = -log10(pvalue_cutoff), col = "green", lwd = 3)
Volcano : Statistical & Biological 3/3

Filtering for DEGs 1/3
filter_by_fold = abs(fold) >= fold_cutoff # Biological
sum(filter_by_fold) # Number of genes staisfy the condition
[1] 1051
filter_by_pvalue = pvalue <= pvalue_cutoff # Statistical
sum(filter_by_pvalue)
[1] 1564
filter_combined = filter_by_fold & filter_by_pvalue # Combined
sum(filter_combined)
[1] 276
Filtering for DEGs 2/3
filtered = data2[filter_combined, ]
dim(filtered)
[1] 276 6
head(filtered)
KO1 KO2 KO3 WT1 WT2 WT3
1416200_at 13.312004 12.973357 12.868456 7.40429 8.558803 8.683696
1416236_a_at 14.148397 14.039236 14.130007 12.23604 12.022402 11.495056
1417808_at 5.321928 5.442943 4.053111 15.16978 15.070087 14.753274
1417932_at 10.602884 10.257152 10.496055 13.98445 14.203294 13.720960
1418050_at 10.622052 10.975490 10.795066 12.86513 13.012048 12.658122
1418100_at 9.117903 8.634811 9.057721 12.90358 12.842449 12.233769
Filtering for DEGs 3/3
plot(-log10(pvalue) ~ fold)
points(-log10(pvalue[filter_combined]) ~ fold[filter_combined],
col = "green")

Exercise
On the volcano plot, highlight the up-regulated genes in red and the download-regulated genes in blue
Heatmap 1/5
heatmap(filtered)

Heatmap 2/5
By default, heatmap clusters genes (rows) and samples (columns) based on the Euclidean distance.
In the context of gene expression, it is more appropriate to cluster the genes and samples based on the correlation to explore patterns of co-regulation (co-expression) among the genes and similarities between samples.
To let heatmap cluster the genes and/or samples, we need to cluster the genes and samples by correlation coefficients (using cor) among the genes and samples.
Heatmap 3/5
# Clustering of the columns (samples)
col_dendrogram = as.dendrogram(hclust(as.dist(1-cor(filtered))))
plot(col_dendrogram)

Heatmap 4/5
# Clustering of the rows (genes)
row_dendrogram = as.dendrogram(hclust(as.dist(1-cor(t(filtered)))))
plot(row_dendrogram)

Heatmap 5/5
# Heatmap with the rows and columns clustered by correlation coefficients
heatmap(filtered, Rowv=row_dendrogram, Colv=col_dendrogram)

Enhanced Heatmap 1/4
There is an enhanced heatmap function heatmap.2 that comes with the gplots package. heatmap.2 provides more options via a richer set of parameters.
install.packages("gplots") # Install the package (if not already installed)
library(gplots) # Load the package
Attaching package: ‘gplots’
The following object is masked from ‘package:stats’:
lowess
Enhanced Heatmap 2/4
heatmap.2(filtered, Rowv=row_dendrogram, Colv=col_dendrogram)

Enhanced Heatmap 3/4
heatmap.2(filtered, Rowv=row_dendrogram, Colv=col_dendrogram,
col = bluered(256))

Enhanced Heatmap 4/4
heatmap.2(filtered, Rowv=row_dendrogram, Colv=col_dendrogram,
col = rev(redgreen(1024)), scale = "row")
Annnotation 1/3
To obtain the functional annotation of the differentially expressed genes, we are going first to extract their probe ids:
filterd_ids = row.names(filtered) # ids of the filtered DE genes
length(filterd_ids)
head(filterd_ids)
Then we can obtain the annotation online via The Database for Annotation, Visualization and Integrated Discovery DAVID.
rr rr
write.table(filterd_ids, file = \filterd_ids.txt\, quote = FALSE)
Now look for a file called “filterd_ids.txt”, open it, copy and paste the ids into DAVID
Annnotation 2/3
Alternatively, we can generate a comprehensive functional annotation via BioConductor packages annaffy and mouse4302.db.
To install BioConductor packages (if they are not already installed):
install.packages("BiocManager")
BiocManager::install(c("annaffy", "mouse4302.db"))
Load the packages and extract annotation of the filtered ids:
library(annaffy)
annotation_table = aafTableAnn(filterd_ids, "mouse4302.db")
saveHTML(annotation_table, file="filtered.html")
browseURL("filtered.html")
Exercise
Generate separate annotations for the up-regulated and down-regulated differentially expressed genes between WT and KO. Discuss each set in a molecular context as detailed as possible.
Homework
Identify the top 10 biologically significant genes (i.e., by fold-change)
Identify the top 10 statistically significant genes (i.e., by p-value)
Identify the overlapping (if any) between the two sets of genes
LS0tCnRpdGxlOiAiR2VuZSBFeHByZXNzaW9uIEFuYWx5c2lzIFVzaW5nIFIiCnN1YnRpdGxlOiAnQ2FzZSBTdHVkeTogSW50ZXJmZXJvbiByZWd1bGF0b3J5IGZhY3RvciA2ICgqSVJGNiopJwpvdXRwdXQ6CiAgcGRmX2RvY3VtZW50OgogICAgZmlnX2NhcHRpb246IHllcwogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIHRvYzogeWVzCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgdG9jOiB5ZXMKbGljZW5zZTogYnktc2EKLS0tCgoKYGBge3IgbGlicmFyaWVzLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KHByaW50cikKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGNvd3Bsb3QpCmBgYAoKIyMgVG9kYXkncyBPYmplY3RpdmVzCi0gRG93bmxvYWQgYW5kIGxvYWQgbWljcm9hcnJheSBkYXRhc2V0IGludG8gUgotIEV4cGxvcmUgdGhlIGRhdGFzZXQgd2l0aCBiYXNpYyB2aXN1YWxpemF0aW9ucwotIElkZW50aWZ5IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyAoREVHcykKLSBHZW5lcmF0ZSBhbm5vdGF0aW9uIG9mIHRoZSBERUdzCgoKPGNlbnRlcj4KIVtdKGltYWdlcy90aXRsZS5wbmcgIk1pY3JvYXJyeSBBbmFseXNpcyB3aXRoIFIiKQo8L2NlbnRlcj4KCgojIyBDbGVmdCBMaXAgYW5kIFBhbGF0ZSAxLzMKCkNsZWZ0IGxpcCBhbmQgY2xlZnQgcGFsYXRlICgqKkNMUCoqKSBhcmUgc3BsaXRzIGluIHRoZSB1cHBlciBsaXAsIHRoZSByb29mIG9mIHRoZSBtb3V0aCAocGFsYXRlKSBvciBib3RoLiBUaGV5IHJlc3VsdCB3aGVuIGZhY2lhbCBzdHJ1Y3R1cmVzIHRoYXQgYXJlIGRldmVsb3BpbmcgaW4gYW4gdW5ib3JuIGJhYnkgZG8gbm90IGNsb3NlIGNvbXBsZXRlbHkuIENMUCBpcyBvbmUgb2YgdGhlIG1vc3QgY29tbW9uIGJpcnRoIGRlZmVjdHMgd2l0aCBhIGZyZXF1ZW5jeSBvZiAxLzcwMCBsaXZlIGJpcnRocy4KCiFbQ2xlZnQgbGlwIGFuZCBwYWxhdGVdKGltYWdlcy9jbGVmdC5qcGcpCgojIyBDbGVmdCBMaXAgYW5kIFBhbGF0ZSAyLzMKCkNoaWxkcmVuIHdpdGggY2xlZnQgbGlwIHdpdGggb3Igd2l0aG91dCBjbGVmdCBwYWxhdGUgZmFjZSBhIHZhcmlldHkgb2YgY2hhbGxlbmdlcywgZGVwZW5kaW5nIG9uIHRoZSB0eXBlIGFuZCBzZXZlcml0eSBvZiB0aGUgY2xlZnQuCgotICoqRGlmZmljdWx0eSBmZWVkaW5nLioqIE9uZSBvZiB0aGUgbW9zdCBpbW1lZGlhdGUgY29uY2VybnMgYWZ0ZXIgYmlydGggaXMgZmVlZGluZy4KCi0gKipFYXIgaW5mZWN0aW9ucyBhbmQgaGVhcmluZyBsb3NzLioqIEJhYmllcyB3aXRoIGNsZWZ0IHBhbGF0ZSBhcmUgZXNwZWNpYWxseSBhdCByaXNrIG9mIGRldmVsb3BpbmcgbWlkZGxlIGVhciBmbHVpZCBhbmQgaGVhcmluZyBsb3NzLgoKLSAqKkRlbnRhbCBwcm9ibGVtcy4qKiBJZiB0aGUgY2xlZnQgZXh0ZW5kcyB0aHJvdWdoIHRoZSB1cHBlciBndW0sIHRvb3RoIGRldmVsb3BtZW50IG1heSBiZSBhZmZlY3RlZC4KCi0gKipTcGVlY2ggZGlmZmljdWx0aWVzLioqIEJlY2F1c2UgdGhlIHBhbGF0ZSBpcyB1c2VkIGluIGZvcm1pbmcgc291bmRzLCB0aGUgZGV2ZWxvcG1lbnQgb2Ygbm9ybWFsIHNwZWVjaCBjYW4gYmUgYWZmZWN0ZWQgYnkgYSBjbGVmdCBwYWxhdGUuIFNwZWVjaCBtYXkgc291bmQgdG9vIG5hc2FsLgoKKlJlZmVyZW5jZSo6IFtNYXlvIEZvdW5kYXRpb24gZm9yIE1lZGljYWwgRWR1Y2F0aW9uIGFuZCBSZXNlYXJjaF0oaHR0cHM6Ly93d3cubWF5b2NsaW5pYy5vcmcvZGlzZWFzZXMtY29uZGl0aW9ucy9jbGVmdC1wYWxhdGUvc3ltcHRvbXMtY2F1c2VzL3N5Yy0yMDM3MDk4NSkKCiMjIENsZWZ0IExpcCBhbmQgUGFsYXRlIDMvMwoKLSBETkEgdmFyaWF0aW9uIGluIEludGVyZmVyb24gUmVndWxhdG9yeSBGYWN0b3IgNiAoKipJUkY2KiopIGNhdXNlcyBWYW4gZGVyIFdvdWRlIHN5bmRyb21lICgqKlZXUyoqKQoKLSBWV1MgaXMgdGhlIG1vc3QgY29tbW9uIHN5bmRyb21pYyBmb3JtIG9mIGNsZWZ0IGxpcCBhbmQgcGFsYXRlLgoKLSBIb3dldmVyLCB0aGUgY2F1c2luZyB2YXJpYW50IGluIElSRjYgaGFzIGJlZW4gZm91bmQgaW4gKm9ubHkqIDcwJSBvZiBWV1MgZmFtaWxpZXMhCgotIElSRjYgaXMgYSAqKnRyYW5zY3JpcHRpb24gZmFjdG9yKiogd2l0aCBhIGNvbnNlcnZlZCBoZWxpeC1sb29wLWhlbGl4IEROQSBiaW5kaW5nIGRvbWFpbiBhbmQgYSBsZXNzIHdlbGwtY29uc2VydmVkIHByb3RlaW4gYmluZGluZyBkb21haW4uIAoKKlJlZmVyZW5jZSo6IFtIdW0gTW9sIEdlbmV0LiAyMDE0IE1heSAxNTsgMjMoMTApOiAyNzEx4oCTMjcyMF0oaHR0cDovL2RvaS5vcmcvMTAuMTA5My9obWcvZGR0NjY0KQoKIyMgUXVlc3Rpb24KCkdpdmVuOgoKMS4gVGhlIHBhdGhvZ2VuaWMgdmFyaWFudCBpbiBJUkY2IGV4aXN0cyBpbiBvbmx5IDcwJSBvZiB0aGUgVldTIGZhbWlsaWVzCgoyLiBJUkY2IGlzIGEgdHJhbnNjcmlwdGlvbiBmYWN0b3IKCkhvdyBjYW4gd2UgaWRlbnRpZnkgb3RoZXIgZ2VuZXMgdGhhdCBtaWdodCBiZSBpbnZvbHZlZCBpbiB0aGUgcmVtYWluaW5nIDMwJSBvZiB0aGUgVldTIGZhbWlsaWVzPwoKIyMgSGludAoKLSBVc3VhbGx5LCBnZW5lcyB0aGF0IGFyZSByZWd1bGF0ZWQgYnkgYSB0cmFuc2NyaXB0aW9uIGZhY3RvciBiZWxvbmcgdG8gdGhlIHNhbWUgYmlvbG9naWNhbCBwcm9jZXNzIG9yIHBhdGh3YXkuCgotIFRoZXJlZm9yZSwgYnkgY29tcGFyaW5nIHRoZSBnZW5lIGV4cHJlc3Npb24gcGF0dGVybnMgYmV0d2VlbiB3aWxkLXR5cGUgKGZ1bmN0aW9uYWwpICpJcmY2KiBhbmQga25vY2tvdXQgKG5vbi1mdW5jdGlvbmFsKSAqSXJmNiosIGl0IGNvdWxkIGJlIHBvc3NpYmxlIHRvIGlkZW50aWZ5IGdlbmVzIHRoYXQgYXJlIHJlZ3VsYXRlZCAodGFyZ2V0ZWQpIGJ5ICpJcmY2Ki4KCiMjIEh5cG90aGVzaXMKCi0gXChIX08gOiBcbXVfe1dUfSA9IFxtdV97S099XCkKCi0gXChIX0EgOiBcbXVfe1dUfSBcbmUgXG11X3tLT31cKQoKLSBXaGVyZSBcKFxtdVwpIGlzIHRoZSAqbWVhbiogb2YgdGhlIGdlbmUgZXhwcmVzc2lvbiB2YWx1ZXMgb2YgYSBnZW5lLgoKLSAqKk9uZSoqLXNpZGVkIG9yICoqVHdvKiotc2lkZWQgdGVzdGluZz8KCmBgYHtyIHNpZGVzLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBmaWcuaGVpZ2h0PTJ9Cm4gPSAxZTYKY3V0b2ZmID0gcW5vcm0oMC4wNSkKbXlkYXRhID0gZGF0YS5mcmFtZSh4ID0gcm5vcm0obikpCmxlZnQgPSBteWRhdGEgJT4lIGZpbHRlcih4IDwgY3V0b2ZmKQpyaWdodCA9IG15ZGF0YSAlPiUgZmlsdGVyKHggPiAtY3V0b2ZmKQpib3RoID0gbXlkYXRhICU+JSBmaWx0ZXIoeCA8IGN1dG9mZiB8IHggPiAtY3V0b2ZmKQoKYmlud2lkdGggPSAxZS0xCgpwID0gZ2dwbG90KCkKcCA9IHAgKyBnZW9tX2hpc3RvZ3JhbShkYXRhID0gbXlkYXRhLCBhZXMoeCA9IHgpLCBmaWxsID0gImdyYXkiLCBiaW53aWR0aCA9IGJpbndpZHRoKQpwID0gcCArIGdlb21faGlzdG9ncmFtKGRhdGEgPSBsZWZ0LCBhZXMoeCA9IHgpLCBmaWxsID0gInJlZCIsIGJpbndpZHRoID0gYmlud2lkdGgpCnAgPSBwICsgbGFicyh4ID0gIiIsIHkgPSAiIikKcCA9IHAgKyB0aGVtZV9saWdodCgpCnAgPSBwICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpCnAgPSBwICsgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCkpCnAgPSBwICsgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkKcDEgPSBwCgpwID0gZ2dwbG90KCkKcCA9IHAgKyBnZW9tX2hpc3RvZ3JhbShkYXRhID0gbXlkYXRhLCBhZXMoeCA9IHgpLCBmaWxsID0gImdyYXkiLCBiaW53aWR0aCA9IGJpbndpZHRoKQpwID0gcCArIGdlb21faGlzdG9ncmFtKGRhdGEgPSByaWdodCwgYWVzKHggPSB4KSwgZmlsbCA9ICJibHVlIiwgYmlud2lkdGggPSBiaW53aWR0aCkKcCA9IHAgKyBsYWJzKHggPSAiIiwgeSA9ICIiKQpwID0gcCArIHRoZW1lX2xpZ2h0KCkKcCA9IHAgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkKcCA9IHAgKyB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSkKcCA9IHAgKyB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKQpwMiA9IHAKCnAgPSBnZ3Bsb3QoKQpwID0gcCArIGdlb21faGlzdG9ncmFtKGRhdGEgPSBteWRhdGEsIGFlcyh4ID0geCksIGZpbGwgPSAiZ3JheSIsIGJpbndpZHRoID0gYmlud2lkdGgpCnAgPSBwICsgZ2VvbV9oaXN0b2dyYW0oZGF0YSA9IGxlZnQsIGFlcyh4ID0geCksIGZpbGwgPSAicmVkIiwgYmlud2lkdGggPSBiaW53aWR0aCkKcCA9IHAgKyBnZW9tX2hpc3RvZ3JhbShkYXRhID0gcmlnaHQsIGFlcyh4ID0geCksIGZpbGwgPSAiYmx1ZSIsIGJpbndpZHRoID0gYmlud2lkdGgpCnAgPSBwICsgbGFicyh4ID0gIiIsIHkgPSAiIikKcCA9IHAgKyB0aGVtZV9saWdodCgpCnAgPSBwICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpCnAgPSBwICsgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCkpCnAgPSBwICsgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkKcDMgPSBwCgpwbG90X2dyaWQocDEsIHAyLCBwMywgbGFiZWxzID0gYygiYSIsICJiIiwgImMiKSwgbmNvbCA9IDMpCmBgYAoKIyMgV2h5IE1pY3JvYXJyYXk/CgotIEl0IGRvZXMgbm90IHJlcXVpcmUgYSBwcmVkZWZpbmllZCBzZXQgb2YgY2FuZGlkYXRlIGdlbmVzCgotIEl0IHJlcXVpcmVzIG9ubHkgYSBzbWFsbCBhbW91bnQgb2YgUk5BCgotIEl0IGlzIGEgaGlnaC10aHJvdWdocHV0IHByb3RvY29sIC0gdHJhbnNjcmlwdG9tZS13aWRlIGFuYWx5c2lzCgotIE9uZSBleHBlcmltZW50IGNhbiBnZW5lcmF0ZSBtdWx0aXBsZSBoeXBvdGhlc2VzCgojIyBPcmlnaW5hbCBQYXBlcgoKIVtQTUlEOiAxNzA0MTYwMV0oaW1hZ2VzL3BtaWQxNzA0MTYwMS5wbmcpCgojIyBFeHBlcmltZW50YWwgRGVzaWduCgotIDMgSVJGNiB3aWxkLXR5cGUgKCsvKykgYW5kIDMga25vY2tvdXQgKC0vLSkgbW91c2UgZW1icnlvcy4KLSBFMTcuNSBlbWJyeW9zIHdlcmUgcmVtb3ZlZCBmcm9tIGV1dGhhbml6ZWQgbW90aGVycy4KLSBTa2luIHdhcyByZW1vdmVkIGZyb20gZW1icnlvcy4KLSBUb3RhbCBSTkEgd2FzIGlzb2xhdGVkIGZyb20gdGhlIHNraW4uCi0gUmVzdWx0YW50IFJOQSB3YXMgaHlicmlkaXplZCB0byBBZmZ5bWV0cml4IEdlbmVDaGlwIE1vdXNlIEdlbm9tZSA0MzAgMi4wIGFycmF5cy4KCiFbXShpbWFnZXMvbWljZS5wbmcpCgojIyBEYXRhc2V0CgotIFRoZSBvcmlnaW5hbCBkYXRhc2V0IGNhbiBiZSBvYnRhaW5lZCBmcm9tIE5DQkkgR0VPIHdpdGggYWNjZXNzaW9uIFtHU0U1ODAwXShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L2dlby9xdWVyeS9hY2MuY2dpP2FjYz1HU0U1ODAwKQoKYGBge3IgZGF0YXNldCwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZGYgPSByZWFkX3RzdigiZGF0YS9JcmY2LnRzdiIpWzE6NCxdCmhlYWQoZGYpCmBgYAoKLSBEb3dubG9hZCB0aGUgZGF0YXNldCBmcm9tIHRoZSBmb2xsb3dpbmcgbGluayBbKipodHRwczovL2dvby5nbC9nSDdRTE0qKl0oaHR0cHM6Ly9nb28uZ2wvZ0g3UUxNKQoKCiMjIExvYWRpbmcKV2UgYXJlIGdvaW5nIHRvIGxvYWQgdGhlIGRhdGFzZXQgZnJvbSBhIHRzdiBmaWxlIChgSXJmNi50c3ZgKSBpbnRvIGEgdmFyaWFibGUgY2FsbGVkIGBkYXRhYCB1c2luZyBmdW5jdGlvbiBbYHJlYWQudGFibGVgXShodHRwOi8vd3d3Lmluc2lkZS1yLm9yZy9yLWRvYy91dGlscy9yZWFkLnRhYmxlKS4KPGJyPgo8YnI+CmBkYXRhYCBoZXJlIGlzIGp1c3QgYW4gYXJiaXRyYXJ5ICoqdmFyaWxhYmxlKiogbmFtZSB0byBob2xkIHRoZSByZXN1bHQgb2YgYHJlYWQudGFibGVgIGFuZCBpdCBjYW4gYmUgY2FsbGVkL25hbWVkICphbG1vc3QqIGFueXRoaW5nLiBTZWUgW1RoZSBTdGF0ZSBvZiBOYW1pbmcgQ29udmVudGlvbnMgaW4gUl0oaHR0cDovL2pvdXJuYWwuci1wcm9qZWN0Lm9yZy9hcmNoaXZlLzIwMTItMi9SSm91cm5hbF8yMDEyLTJfQmFhYWF0aC5wZGYpIChCw6XDpXRoIDIwMTIpIGZvciBtb3JlIGluZm9ybWF0aW9uIG9uIG5hbWluZyAqKnZhcmlsYWJsZXMqKiBpbiBgUmAuCgpgYGB7cn0KIyBMb2FkIGRhdGEgZnJvbSB0ZXh0IGZpbGUgaW50byBhIHZhcmlsYWJsZQpkYXRhID0gYXMubWF0cml4KHJlYWQudGFibGUoIklyZjYudHN2IiwgaGVhZGVyID0gVFJVRSwgcm93Lm5hbWVzID0gMSkpCmBgYAoKKipOb3RlOioqIHRoZSBoYXNoIHNpZ24gKGAjYCkgaW5kaWNhdGVzIHRoYXQgd2hhdCBjb21lcyBhZnRlciBpcyBhICpjb21tZW50Ki4gQ29tbWVudHMgYXJlIGZvciBkb2N1bWVudGF0aW9uIGFuZCByZWFkYWJpbGl0eSBvZiB0aGUgYFJgIGNvZGUgYW5kIHRoZXkgYXJlIG5vdCBldmFsdWF0ZWQgKG9yIGV4ZWN1dGVkKS4KCiMjIENoZWNraW5nCgpgYGB7cn0KZGltKGRhdGEpICMgRGltZW5zaW9uIG9mIHRoZSBkYXRhc2V0CmhlYWQoZGF0YSkgIyBGaXJzdCBmZXcgcm93cwpgYGAKCiMjIEV4cGxvcmluZwpDaGVjayB0aGUgYmVoYXZpb3Igb2YgdGhlIGRhdGEgKGUuZy4sIG5vcm1hbD8sIHNrZXdlZD8pCgpgYGB7cn0KaGlzdChkYXRhLCBjb2wgPSAiZ3JheSIsIG1haW49Ikhpc3RvZ3JhbSIpCmBgYAoKIyMgVHJhbnNmb3JtaW5nCgpcKGxvZ18yXCkgdHJhbnNmb3JtYXRpb24gKHdoeT8pCgpgYGB7cn0KZGF0YTIgPSBsb2cyKGRhdGEpCmhpc3QoZGF0YTIsIGNvbCA9ICJncmF5IiwgbWFpbj0iSGlzdG9ncmFtIikKYGBgCgojIyBNdWx0aXBsZSBQbG90cyAxLzIKYGBge3J9CnNhbXBsZXMgPSBjb2xuYW1lcyhkYXRhMikgIyBIZWFkZXJzIChuYW1lcykgb2YgdGhlIGNvbHVtbnMKc2FtcGxlcwpgYGAKCmBgYHtyIGV2YWw9RkFMU0V9CnBhciggbWZyb3cgPSBjKCAyLCAzICkgKSAjIFNwbGl0IHNjcmVlbiBpbnRvIDIgcm93cyB4IDMgY29sdW1ucyBwYXJpdGlvbnMKCmZvciAoaSBpbiAxOjMpIHsKICAjIGZvciBlYWNoIG9mIHRoZSBmaXJzdCAzIGNvbHVtbnMgaW4gdGhlIHRhYmxlCiAgaGlzdChkYXRhMlssaV0sIGNvbCA9ICJyZWQiLCBtYWluID0gc2FtcGxlc1tpXSkKfQoKZm9yIChpIGluIDQ6NikgewogICMgZm9yIGVhY2ggb2YgdGhlIGxhc3QgMyBjb2x1bW5zIGluIHRoZSB0YWJsZQogIGhpc3QoZGF0YTJbLGldLCBjb2wgPSAiZ3JlZW4iLCBtYWluID0gc2FtcGxlc1tpXSkKfQpgYGAKCiMjIE11bHRpcGxlIFBsb3RzIDIvMgpgYGB7ciBlY2hvPUZBTFNFfQpwYXIoIG1mcm93ID0gYyggMiwgMyApICkgIyBTcGxpdCBzY3JlZW4gaW50byAyIHJvd3MgeCAzIGNvbHVtbnMgcGFyaXRpb25zCgpmb3IgKGkgaW4gMTozKSB7CiAgIyBmb3IgZWFjaCBvZiB0aGUgZmlyc3QgMyBjb2x1bW5zIGluIHRoZSB0YWJsZQogIGhpc3QoZGF0YTJbLGldLCBjb2wgPSAiZGFya2dyZWVuIiwgbWFpbiA9IHNhbXBsZXNbaV0pCn0KCmZvciAoaSBpbiA0OjYpIHsKICAjIGZvciBlYWNoIG9mIHRoZSBsYXN0IDMgY29sdW1ucyBpbiB0aGUgdGFibGUKICBoaXN0KGRhdGEyWyxpXSwgY29sID0gIm9yYW5nZSIsIG1haW4gPSBzYW1wbGVzW2ldKQp9CmBgYAoKYGBge3J9CnBhciggbWZyb3cgPSBjKCAxLCAxICkgKSAjIEp1c3QgdG8gc2V0IHNjcmVlbiBiYWNrIHRvIDEgcGFydGl0aW9uCmBgYAoKIyMgQm94cGxvdAoKYGBge3J9CmNvbG9ycyA9IGMocmVwKCJyZWQiLCAzKSwgcmVwKCJncmVlbiIsIDMpKQpib3hwbG90KGRhdGEyLCBjb2wgPSBjb2xvcnMsIGxhcyA9IDIpCmBgYAoKIyMgQ2x1c3RlcmluZyAxLzIKCkhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIG9mIHRoZSAqKnNhbXBsZXMqKiAoaS5lLiwgY29sdW1ucykgYmFzZWQgb24gdGhlIFtjb3JyZWxhdGlvbiBjb2VmZmljaWVudHNdKGh0dHA6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvUGVhcnNvbl9wcm9kdWN0LW1vbWVudF9jb3JyZWxhdGlvbl9jb2VmZmljaWVudCkgb2YgdGhlIGV4cHJlc3Npb24gdmFsdWVzCgpgYGB7cn0KaGMgPSBoY2x1c3QoYXMuZGlzdCgxIC0gY29yKGRhdGEyKSkpCnBsb3QoaGMpCmBgYAoKIyMgQ2x1c3RlcmluZyAyLzIKVG8gbGVhcm4gYWJvdXQgYSBmdW5jdGlvbiAoZS5nLiwgYGhjbHVzdGApLCB5b3UgbWF5IHR5cGUgYD9mdW5jdGlvbmAgKGUuZy4sIGA/aGNsdXN0YCkgaW4gdGhlIGBjb25zb2xlYCB0byBsYXVuY2ggYFJgIGRvY3VtZW50YXRpb24gb24gdGhhdCBmdW5jdGlvbjoKCiMjIENvbXBhcmluZwoKV2UgYXJlIGdvaW5nIHRvIGNvbXBhcmUgdGhlICoqbWVhbnMqKiBvZiB0aGUgcmVwbGljYXRlcyBvZiB0aGUgdHdvIGNvbmRpdGlvbnMKCmBgYHtyfQojIENvbXB1dGUgdGhlIG1lYW5zIG9mIHRoZSBzYW1wbGVzIG9mIGVhY2ggY29uZGl0aW9uCmtvID0gYXBwbHkoZGF0YTJbLCAxOjNdLCAxLCBtZWFuKQpoZWFkKGtvKQoKd3QgPSBhcHBseShkYXRhMlssIDQ6Nl0sIDEsIG1lYW4pCmhlYWQod3QpCmBgYAoKIyMgU2NhdHRlcgpgYGB7ciBlY2hvPUZBTFNFfQpwbG90KGtvIH4gd3QpCmFibGluZSgwLCAxLCBjb2wgPSAicmVkIikgIyBEaWFnb25hbCBsaW5lCmBgYAoKIyMgRGlmZmVyZW50aWFsbHkgRXhwcmVzc2VkIEdlbmVzIChERUdzKQoKVG8gaWRlbnRpZnkgREVHcywgd2Ugd2lsbCBpZGVudGlmeToKCi0gKipCaW9sb2dpY2FsbHkqKiBzaWduaWZpY2FudGx5IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZAotICoqU3RhdGlzdGljYWxseSoqIHNpZ25pZmljYW50bHkgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkCgpUaGVuLCB3ZSB3aWxsIHRha2UgdGhlICoqb3ZlcmxhcCoqICgqKmludGVyc2VjdGlvbioqKSBvZiB0aGUgdHdvIHNldHMKCiMjIEJpb2xvZ2ljYWwgU2lnbmlmaWNhbmNlIChmb2xkLWNoYW5nZSkgMS8yCmBgYHtyfQpmb2xkID0ga28gLSB3dCAjIERpZmZlcmVuY2UgYmV0d2VlbiBtZWFucwpoZWFkKGZvbGQpCmBgYAoKLSBXaGF0IGRvIHRoZSBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUgdmFsdWVzIG9mIHRoZSBmb2xkLWNoYW5nZSBpbmRpY2F0ZT8gQ29uc2lkZXJpbmcgdGhlIGBXVGAgY29uZGl0aW9uIGlzIHRoZSAqKnJlZmVyZW5jZSoqIChvciAqKmNvbnRyb2wqKikKCj4gLSAqKit2ZSoqIFwoXHJpZ2h0YXJyb3dcKSAqKlVwKiotcmVndWxhdGlvbiBcKFx1cGFycm93XCkKPiAtICoqLXZlKiogXChccmlnaHRhcnJvd1wpICoqRG93bioqLXJlZ3VsYXRpb24gXChcZG93bmFycm93XCkKCiMjIEJpb2xvZ2ljYWwgU2lnbmlmaWNhbmNlIChmb2xkLWNoYW5nZSkgMi8yCmBgYHtyfQpoaXN0KGZvbGQsIGNvbCA9ICJncmF5IikgIyBIaXN0b2dyYW0gb2YgdGhlIGZvbGQKYGBgCgojIyBTdGF0aXN0aWNhbCBTaWduaWZpY2FuY2UgKCpwKi12YWx1ZSkgMS8zCi0gVG8gYXNzZXNzIHRoZSBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2Ugb2YgdGhlIGRpZmZlcmVuY2UgaW4gdGhlIGV4cHJlc3Npb24gdmFsdWVzIGZvciBlYWNoIGdlbmUgYmV0d2VlbiB0aGUgdHdvIGNvbmRpdGlvbnMgKGUuZy4sIGBXVGAgYW5kIGBLT2ApLCB3ZSBhcmUgZ29pbmcgdG8gdXNlIFsqdCotdGVzdF0oaHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9TdHVkZW50JTI3c190LXRlc3QpLgoKYGBge3IgZWNobz1GQUxTRSxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0UsZmlnLmhlaWdodD0zfQpuID0gMWU2CnggPSBkYXRhLmZyYW1lKFZhbHVlID0gcm5vcm0obiwgbSA9IDAuNSwgc2QgPSAxKSwgUG9wID0gIlgiKQp5ID0gZGF0YS5mcmFtZShWYWx1ZSA9IHJub3JtKG4sIG0gPSAtMC41LCBzZCA9IDEpLCBQb3AgPSAiWSIpCnogPSB4ICU+JSBiaW5kX3Jvd3MoeSkKcCA9IGdncGxvdCh6KQpwID0gcCArIGdlb21fZGVuc2l0eShhZXMoeCA9IFZhbHVlLCBmaWxsID0gUG9wKSwgYWxwaGEgPSAwLjUpCnAgPSBwICsgdGhlbWVfbGlnaHQoKQpwID0gcCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpwMSA9IHAKCnggPSBkYXRhLmZyYW1lKFZhbHVlID0gcm5vcm0obiwgbSA9IDIsIHNkID0gMSksIFBvcCA9ICJYIikKeSA9IGRhdGEuZnJhbWUoVmFsdWUgPSBybm9ybShuLCBtID0gLTIsIHNkID0gMSksIFBvcCA9ICJZIikKeiA9IHggJT4lIGJpbmRfcm93cyh5KQpwID0gZ2dwbG90KHopCnAgPSBwICsgZ2VvbV9kZW5zaXR5KGFlcyh4ID0gVmFsdWUsIGZpbGwgPSBQb3ApLCBhbHBoYSA9IDAuNSkKcCA9IHAgKyB0aGVtZV9saWdodCgpCnAgPSBwICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCnAyID0gcAoKcGxvdF9ncmlkKHAxLCBwMiwgbGFiZWxzID0gYygiYSIsICJiIikpCgpgYGAKCiMjICp0Ki10ZXN0CgpMZXQncyBzYXkgdGhlcmUgYXJlIHR3byBzYW1wbGVzICp4KiBhbmQgKnkqIGZyb20gdGhlIHR3byBwb3B1bGF0aW9ucywgKlgqIGFuZCAqWSosIHJlc3BlY3RpdmVseSwgdG8gZGV0ZXJtaW5lIHdoZXRoZXIgdGhlIG1lYW5zIG9mIHR3byBwb3B1bGF0aW9ucyBhcmUgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQsIHdlIGNhbiB1c2UgYHQudGVzdGAuCgpgYGB7cn0KP3QudGVzdApgYGAKCiMjICp0Ki10ZXN0IDogRXhhbXBsZSAxCgpgYGB7cn0KeCA9IGMoNCwgMywgMTAsIDcsIDkpIDsgeSA9IGMoNywgNCwgMywgOCwgMTApCnQudGVzdCh4LCB5KQpgYGAKCmBgYHtyfQp0LnRlc3QoeCwgeSkkcC52YWx1ZQpgYGAKCiMjICp0Ki10ZXN0IDogRXhhbXBsZSAyCgpgYGB7cn0KeCA9IGMoNiwgOCwgMTAsIDcsIDkpIDsgeSA9IGMoMywgMiwgMSwgNCwgNSkKdC50ZXN0KHgsIHkpCmBgYAoKYGBge3J9CnQudGVzdCh4LCB5KSRwLnZhbHVlCmBgYAoKIyMgU3RhdGlzdGljYWwgU2lnbmlmaWNhbmNlICgqcCotdmFsdWUpIDIvMwpgYGB7cn0KcHZhbHVlID0gTlVMTCAjIEVtcHR5IGxpc3QgZm9yIHRoZSBwLXZhbHVlcwpuID0gbnJvdyhkYXRhKSAjIE51bWJlciBvZiBnZW5lcyAocm93cykKbgpmb3IoaSBpbiAxIDogbikgewogICMgZm9yIGVhY2ggZ2VuZQogIHggPSBkYXRhMltpLCAxOjNdICMgd3QgdmFsdWVzIG9mIGdlbmUgbnVtYmVyIGkKICB5ID0gZGF0YTJbaSwgNDo2XSAjIGtvIHZhbHVlcyBvZiBnZW5lIG51bWJlciBpCiAgdCA9IHQudGVzdCh4LCB5KSAjIHQtdGVzdCBiZXR3ZWVuIHRoZSB0d28gY29uZGl0aW9ucwogIHB2YWx1ZVtpXSA9IHQkcC52YWx1ZSAjIFB1dCBjdXJyZW50IHAtdmFsdWUgaW4gdGhlIGxpc3Qgb2YgcC12YWx1ZXMKfQpoZWFkKHB2YWx1ZSkKYGBgCgojIyBTdGF0aXN0aWNhbCBTaWduaWZpY2FuY2UgKCpwKi12YWx1ZSkgMy8zCmBgYHtyfQpoaXN0KC1sb2cxMChwdmFsdWUpLCBjb2wgPSAiZ3JheSIpICMgSGlzdG9ncmFtIG9mIHAtdmFsdWVzICgtbG9nMTApCmBgYAoKIyMgVm9sY2FubyA6IFN0YXRpc3RpY2FsICYgQmlvbG9naWNhbCAxLzMKYGBge3J9CnBsb3QoLWxvZzEwKHB2YWx1ZSkgfiBmb2xkKQpgYGAKCiMjIFZvbGNhbm8gOiBTdGF0aXN0aWNhbCAmIEJpb2xvZ2ljYWwgMi8zCmBgYHtyIGV2YWw9RkFMU0V9CmZvbGRfY3V0b2ZmID0gMgpwdmFsdWVfY3V0b2ZmID0gMC4wMQoKcGxvdCgtbG9nMTAocHZhbHVlKSB+IGZvbGQpCgphYmxpbmUodiA9IGZvbGRfY3V0b2ZmLCBjb2wgPSAiYmx1ZSIsIGx3ZCA9IDMpCmFibGluZSh2ID0gLWZvbGRfY3V0b2ZmLCBjb2wgPSAicmVkIiwgbHdkID0gMykKYWJsaW5lKGggPSAtbG9nMTAocHZhbHVlX2N1dG9mZiksIGNvbCA9ICJncmVlbiIsIGx3ZCA9IDMpCmBgYAoKIyMgVm9sY2FubyA6IFN0YXRpc3RpY2FsICYgQmlvbG9naWNhbCAzLzMKYGBge3IgZWNobz1GQUxTRX0KZm9sZF9jdXRvZmYgPSAyCnB2YWx1ZV9jdXRvZmYgPSAwLjAxCgpwbG90KC1sb2cxMChwdmFsdWUpIH4gZm9sZCkKCmFibGluZSh2ID0gZm9sZF9jdXRvZmYsIGNvbCA9ICJibHVlIiwgbHdkID0gMykKYWJsaW5lKHYgPSAtZm9sZF9jdXRvZmYsIGNvbCA9ICJyZWQiLCBsd2QgPSAzKQphYmxpbmUoaCA9IC1sb2cxMChwdmFsdWVfY3V0b2ZmKSwgY29sID0gImdyZWVuIiwgbHdkID0gMykKYGBgCgoKIyMgRmlsdGVyaW5nIGZvciBERUdzIDEvMwpgYGB7cn0KZmlsdGVyX2J5X2ZvbGQgPSBhYnMoZm9sZCkgPj0gZm9sZF9jdXRvZmYgIyBCaW9sb2dpY2FsCnN1bShmaWx0ZXJfYnlfZm9sZCkgIyBOdW1iZXIgb2YgZ2VuZXMgc3RhaXNmeSB0aGUgY29uZGl0aW9uCgpmaWx0ZXJfYnlfcHZhbHVlID0gcHZhbHVlIDw9IHB2YWx1ZV9jdXRvZmYgIyBTdGF0aXN0aWNhbApzdW0oZmlsdGVyX2J5X3B2YWx1ZSkKCmZpbHRlcl9jb21iaW5lZCA9IGZpbHRlcl9ieV9mb2xkICYgZmlsdGVyX2J5X3B2YWx1ZSAjIENvbWJpbmVkCnN1bShmaWx0ZXJfY29tYmluZWQpCmBgYAoKIyMgRmlsdGVyaW5nIGZvciBERUdzIDIvMwpgYGB7cn0KZmlsdGVyZWQgPSBkYXRhMltmaWx0ZXJfY29tYmluZWQsIF0KZGltKGZpbHRlcmVkKQpoZWFkKGZpbHRlcmVkKQpgYGAKCiMjIEZpbHRlcmluZyBmb3IgREVHcyAzLzMKYGBge3J9CnBsb3QoLWxvZzEwKHB2YWx1ZSkgfiBmb2xkKQpwb2ludHMoLWxvZzEwKHB2YWx1ZVtmaWx0ZXJfY29tYmluZWRdKSB+IGZvbGRbZmlsdGVyX2NvbWJpbmVkXSwKICAgICAgIGNvbCA9ICJncmVlbiIpCmBgYAoKIyMgRXhlcmNpc2UKT24gdGhlIHZvbGNhbm8gIHBsb3QsIGhpZ2hsaWdodCB0aGUgdXAtcmVndWxhdGVkIGdlbmVzIGluIHJlZCBhbmQgdGhlIGRvd25sb2FkLXJlZ3VsYXRlZCBnZW5lcyBpbiBibHVlCgoKIyMgSGVhdG1hcCAxLzUKYGBge3J9CmhlYXRtYXAoZmlsdGVyZWQpCmBgYAoKIyMgSGVhdG1hcCAyLzUKLSBCeSBkZWZhdWx0LCBgaGVhdG1hcGAgY2x1c3RlcnMgZ2VuZXMgKHJvd3MpIGFuZCBzYW1wbGVzIChjb2x1bW5zKSBiYXNlZCBvbiBbdGhlIEV1Y2xpZGVhbiBkaXN0YW5jZV0oaHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9FdWNsaWRlYW5fZGlzdGFuY2UpLgoKLSBJbiB0aGUgY29udGV4dCBvZiBnZW5lIGV4cHJlc3Npb24sIGl0IGlzIG1vcmUgYXBwcm9wcmlhdGUgdG8gY2x1c3RlciB0aGUgZ2VuZXMgYW5kIHNhbXBsZXMgYmFzZWQgb24gdGhlIGNvcnJlbGF0aW9uIHRvIGV4cGxvcmUgcGF0dGVybnMgb2YgKipbY28tcmVndWxhdGlvbl0oaHR0cDovL2R4LmRvaS5vcmcvMTAuMTE4Ni8xNDcxLTIxMDUtNS0xOCkqKiAoKipjby1leHByZXNzaW9uKiopIGFtb25nIHRoZSBnZW5lcyBhbmQgc2ltaWxhcml0aWVzIGJldHdlZW4gc2FtcGxlcy4KCi0gVG8gbGV0IGBoZWF0bWFwYCBjbHVzdGVyIHRoZSBnZW5lcyBhbmQvb3Igc2FtcGxlcywgd2UgbmVlZCB0byBjbHVzdGVyIHRoZSBnZW5lcyBhbmQgc2FtcGxlcyBieSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMgKHVzaW5nIGBjb3JgKSBhbW9uZyB0aGUgZ2VuZXMgYW5kIHNhbXBsZXMuCgojIyBIZWF0bWFwIDMvNQoKYGBge3J9CiMgQ2x1c3RlcmluZyBvZiB0aGUgY29sdW1ucyAoc2FtcGxlcykKY29sX2RlbmRyb2dyYW0gPSBhcy5kZW5kcm9ncmFtKGhjbHVzdChhcy5kaXN0KDEtY29yKGZpbHRlcmVkKSkpKQpwbG90KGNvbF9kZW5kcm9ncmFtKQpgYGAKCiMjIEhlYXRtYXAgNC81CmBgYHtyfQojIENsdXN0ZXJpbmcgb2YgdGhlIHJvd3MgKGdlbmVzKQpyb3dfZGVuZHJvZ3JhbSA9IGFzLmRlbmRyb2dyYW0oaGNsdXN0KGFzLmRpc3QoMS1jb3IodChmaWx0ZXJlZCkpKSkpCnBsb3Qocm93X2RlbmRyb2dyYW0pCmBgYAoKIyMgSGVhdG1hcCA1LzUKYGBge3J9CiMgSGVhdG1hcCB3aXRoIHRoZSByb3dzIGFuZCBjb2x1bW5zIGNsdXN0ZXJlZCBieSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMKaGVhdG1hcChmaWx0ZXJlZCwgUm93dj1yb3dfZGVuZHJvZ3JhbSwgQ29sdj1jb2xfZGVuZHJvZ3JhbSkKYGBgCgojIyBFbmhhbmNlZCBIZWF0bWFwIDEvNApUaGVyZSBpcyBhbiAqZW5oYW5jZWQqIGhlYXRtYXAgZnVuY3Rpb24gYGhlYXRtYXAuMmAgdGhhdCBjb21lcyB3aXRoIHRoZSBgZ3Bsb3RzYCBwYWNrYWdlLiBgaGVhdG1hcC4yYCBwcm92aWRlcyBtb3JlIG9wdGlvbnMgdmlhIGEgcmljaGVyIHNldCBvZiBwYXJhbWV0ZXJzLgoKYGBge3IgZXZhbD1GQUxTRX0KaW5zdGFsbC5wYWNrYWdlcygiZ3Bsb3RzIikgIyBJbnN0YWxsIHRoZSBwYWNrYWdlIChpZiBub3QgYWxyZWFkeSBpbnN0YWxsZWQpCmBgYAoKYGBge3J9CmxpYnJhcnkoZ3Bsb3RzKSAjIExvYWQgdGhlIHBhY2thZ2UKYGBgCgojIyBFbmhhbmNlZCBIZWF0bWFwIDIvNApgYGB7cn0KaGVhdG1hcC4yKGZpbHRlcmVkLCBSb3d2PXJvd19kZW5kcm9ncmFtLCBDb2x2PWNvbF9kZW5kcm9ncmFtKQpgYGAKCiMjIEVuaGFuY2VkIEhlYXRtYXAgMy80CmBgYHtyfQpoZWF0bWFwLjIoZmlsdGVyZWQsIFJvd3Y9cm93X2RlbmRyb2dyYW0sIENvbHY9Y29sX2RlbmRyb2dyYW0sCiAgICAgICAgICBjb2wgPSBibHVlcmVkKDI1NikpCmBgYAoKIyMgRW5oYW5jZWQgSGVhdG1hcCA0LzQKYGBge3J9CmhlYXRtYXAuMihmaWx0ZXJlZCwgUm93dj1yb3dfZGVuZHJvZ3JhbSwgQ29sdj1jb2xfZGVuZHJvZ3JhbSwKICAgICAgICAgIGNvbCA9IHJldihyZWRncmVlbigxMDI0KSksIHNjYWxlID0gInJvdyIpCgpgYGAKCiMjIEFubm5vdGF0aW9uIDEvMwpUbyBvYnRhaW4gdGhlIGZ1bmN0aW9uYWwgYW5ub3RhdGlvbiBvZiB0aGUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzLCB3ZSBhcmUgZ29pbmcgZmlyc3QgdG8gZXh0cmFjdCB0aGVpciBwcm9iZSBpZHM6CmBgYHtyfQpmaWx0ZXJkX2lkcyA9IHJvdy5uYW1lcyhmaWx0ZXJlZCkgIyBpZHMgb2YgdGhlIGZpbHRlcmVkIERFIGdlbmVzCmxlbmd0aChmaWx0ZXJkX2lkcykKaGVhZChmaWx0ZXJkX2lkcykKYGBgCgpUaGVuIHdlIGNhbiBvYnRhaW4gdGhlIGFubm90YXRpb24gb25saW5lIHZpYSBUaGUgRGF0YWJhc2UgZm9yIEFubm90YXRpb24sIFZpc3VhbGl6YXRpb24gYW5kIEludGVncmF0ZWQgRGlzY292ZXJ5IFtEQVZJRF0oaHR0cDovL2RhdmlkLmFiY2MubmNpZmNyZi5nb3YvKS4KCmBgYHtyfQp3cml0ZS50YWJsZShmaWx0ZXJkX2lkcywgZmlsZSA9ICJmaWx0ZXJkX2lkcy50eHQiLCBxdW90ZSA9IEZBTFNFLCByb3cubmFtZXMgPSBGQUxTRSwgY29sLm5hbWVzID0gRkFMU0UpCmBgYAoKTm93IGxvb2sgZm9yIGEgZmlsZSBjYWxsZWQgImZpbHRlcmRfaWRzLnR4dCIsIG9wZW4gaXQsIGNvcHkgYW5kIHBhc3RlIHRoZSBpZHMgaW50byBEQVZJRAoKCiMjIEFubm5vdGF0aW9uIDIvMwpBbHRlcm5hdGl2ZWx5LCB3ZSBjYW4gZ2VuZXJhdGUgYSBjb21wcmVoZW5zaXZlIGZ1bmN0aW9uYWwgYW5ub3RhdGlvbiB2aWEgYEJpb0NvbmR1Y3RvcmAgcGFja2FnZXMgYGFubmFmZnlgIGFuZCBgbW91c2U0MzAyLmRiYC4KClRvIGluc3RhbGwgYEJpb0NvbmR1Y3RvcmAgcGFja2FnZXMgKGlmIHRoZXkgYXJlIG5vdCBhbHJlYWR5IGluc3RhbGxlZCk6CgpgYGB7ciBldmFsPUZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKCJCaW9jTWFuYWdlciIpCkJpb2NNYW5hZ2VyOjppbnN0YWxsKGMoImFubmFmZnkiLCAibW91c2U0MzAyLmRiIikpCmBgYAoKTG9hZCB0aGUgcGFja2FnZXMgYW5kIGV4dHJhY3QgYW5ub3RhdGlvbiBvZiB0aGUgZmlsdGVyZWQgaWRzOgoKYGBge3IgZXZhbD1GQUxTRX0KbGlicmFyeShhbm5hZmZ5KQphbm5vdGF0aW9uX3RhYmxlID0gYWFmVGFibGVBbm4oZmlsdGVyZF9pZHMsICJtb3VzZTQzMDIuZGIiKQpzYXZlSFRNTChhbm5vdGF0aW9uX3RhYmxlLCBmaWxlPSJmaWx0ZXJlZC5odG1sIikKYnJvd3NlVVJMKCJmaWx0ZXJlZC5odG1sIikKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpsaWJyYXJ5KGFubmFmZnkpCmFubm90YXRpb25fdGFibGUgPSBhYWZUYWJsZUFubihmaWx0ZXJkX2lkcywgIm1vdXNlNDMwMi5kYiIpCmhlYWQoYW5ub3RhdGlvbl90YWJsZSkKc2F2ZUhUTUwoYW5ub3RhdGlvbl90YWJsZSwgZmlsZT0iZmlsdGVyZWQuaHRtbCIpCmJyb3dzZVVSTCgiZmlsdGVyZWQuaHRtbCIpCmBgYAoKIyMgQW5ubm90YXRpb24gMy8zCiFbQW5ub3RhdGlvbl0oaW1hZ2VzL2FmZnkucG5nICJGdW5jdGlvbmFsIEFubm90YXRpb24iKQoKIyMgRXhlcmNpc2UKR2VuZXJhdGUgc2VwYXJhdGUgYW5ub3RhdGlvbnMgZm9yIHRoZSB1cC1yZWd1bGF0ZWQgYW5kIGRvd24tcmVndWxhdGVkIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBiZXR3ZWVuIGBXVGAgYW5kIGBLT2AuIERpc2N1c3MgZWFjaCBzZXQgaW4gYSBtb2xlY3VsYXIgY29udGV4dCBhcyBkZXRhaWxlZCBhcyBwb3NzaWJsZS4KCiMjIEhvbWV3b3JrCi0gSWRlbnRpZnkgdGhlIHRvcCAxMCAqYmlvbG9naWNhbGx5KiBzaWduaWZpY2FudCBnZW5lcyAoaS5lLiwgYnkgZm9sZC1jaGFuZ2UpCgotIElkZW50aWZ5IHRoZSB0b3AgMTAgKnN0YXRpc3RpY2FsbHkqIHNpZ25pZmljYW50IGdlbmVzIChpLmUuLCBieSAqcCotdmFsdWUpCgotIElkZW50aWZ5IHRoZSBvdmVybGFwcGluZyAoaWYgYW55KSBiZXR3ZWVuIHRoZSB0d28gc2V0cyBvZiBnZW5lcwoK